import com.google.common.base.Strings;
import com.google.common.io.Files;
import hdf.parsers.HdfParser;
import hdf.parsers.modis.L2SurfaceReflectance;
import hdf.utils.HdfFileHelper;
import hdf.utils.LatLonPair;
import hdf.utils.LatLonRange;
import ncsa.hdf.object.Dataset;
import ncsa.hdf.object.FileFormat;
import ncsa.hdf.object.Group;

import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeNode;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;

/**
 * Created by Ania on 15.03.14.
 */
public class MainWindow {
    private JTextField txtFile;
    private JButton btnFile;
    private JPanel mainPanel;
    private JTextArea taLog;
    private JScrollPane scrollPane;
    private JComboBox cbParsers;
    private JComboBox cbDatasets;
    private JTextField txtLatMin;
    private JTextField txtLatMax;
    private JButton btnConvert;
    private JTextField txtLonMin;
    private JTextField txtLonMax;
    private JButton btnCrop;
    private String openedFile = null;
    private FileFormat openedFileFormat = null;
    private HashMap<String, Dataset> datasetMap;
    private HdfParser[] parsers = new HdfParser[]{L2SurfaceReflectance.getInstance()};
    private HdfParser currentParser;
    private String currentDataset;
    private LatLonRange currentLatLonRange = null;

    public MainWindow() {
        setListeners();
        initCombos();
    }

    private void initCombos() {
        setComboboxModel(cbParsers, parsers);
        currentParser = parsers[0];
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame("MainWindow");
        try {
            UIManager.setLookAndFeel(
                    UIManager.getSystemLookAndFeelClassName());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (UnsupportedLookAndFeelException e) {
            e.printStackTrace();
        }
        frame.setContentPane(new MainWindow().mainPanel);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setVisible(true);
    }

    private void setListeners() {
        btnFile.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                clearLog();
                initMaps();
                chooseFile();
                log(HdfFileHelper.datasetInfo(datasetMap.get(currentDataset), openedFile, openedFileFormat));
            }
        });

        btnConvert.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                showSaveImageDialog();
            }
        });

        btnCrop.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                String latMinText = txtLatMin.getText();
                String latMaxText = txtLatMax.getText();
                String lonMinText = txtLonMin.getText();
                String lonMaxText = txtLonMax.getText();
                float latMin, latMax, lonMin, lonMax;
                try {
                    latMin = Float.parseFloat(latMinText);
                    latMax = Float.parseFloat(latMaxText);
                    lonMin = Float.parseFloat(lonMinText);
                    lonMax = Float.parseFloat(lonMaxText);
                } catch (NumberFormatException ex) {
                    JOptionPane.showMessageDialog(null, "Invalid value in one of the lat/lon fields.");
                    return;
                }
                LatLonPair min = new LatLonPair(latMin, lonMin);
                LatLonPair max = new LatLonPair(latMax, lonMax);
                LatLonRange enteredLatLonRange = new LatLonRange(min, max);
                if (currentLatLonRange == null) {
                    JOptionPane.showMessageDialog(null, "Lat/lon range of the file was not read.");
                } else if (currentLatLonRange.contains(enteredLatLonRange)) {
                    showSaveImageDialog(enteredLatLonRange);
                } else {
                    JOptionPane.showMessageDialog(null, "Lat/lon range out of bounds.");
                }
            }
        });

        cbParsers.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                final JComboBox source = (JComboBox) e.getSource();
                currentParser = (HdfParser) source.getSelectedItem();
            }
        });

        cbDatasets.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                final JComboBox source = (JComboBox) e.getSource();
                currentDataset = (String) source.getSelectedItem();
                Dataset obj = datasetMap.get(currentDataset);
                log(HdfFileHelper.datasetInfo(obj, openedFile, openedFileFormat));
            }
        });
    }

    private void showSaveImageDialog() {
        showSaveImageDialog(null);
    }

    private void showSaveImageDialog(LatLonRange range) {
        JFileChooser fc = new JFileChooser();
        FileNameExtensionFilter filter = new FileNameExtensionFilter(
                "Images", "jpg", "png", "bmp");
        fc.setFileFilter(filter);
        if (fc.showSaveDialog(mainPanel) == JFileChooser.APPROVE_OPTION) {
            convertToImage(fc.getSelectedFile(), range);
        }
    }

    private void convertToImage(File saveFile, LatLonRange range) {
        BufferedImage img = currentParser.convertToImage(openedFile, openedFileFormat, currentDataset, range);
        String extension = Files.getFileExtension(saveFile.getPath());
        if (Strings.isNullOrEmpty(extension)) extension = "png";
        if (img != null) {
            try {
                ImageIO.write(img, extension, saveFile);
                JOptionPane.showMessageDialog(null, "Image successfully saved.");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void setComboboxModel(JComboBox comboBox, Object[] items) {
        MutableComboBoxModel parsersModel = new DefaultComboBoxModel(items);
        comboBox.setModel(parsersModel);
    }

    private void clearLog() {
        taLog.setText("");
    }

    private void initMaps() {
        datasetMap = new HashMap<String, Dataset>();
    }

    private void chooseFile() {
        JFileChooser fc = new JFileChooser();
        FileNameExtensionFilter filter = new FileNameExtensionFilter(
                "HDF data", "hdf", "h5");
        fc.setFileFilter(filter);
        fc.setCurrentDirectory(new File("D:\\Studies\\MGR\\Magisterka\\data"));
        if (fc.showOpenDialog(mainPanel) == JFileChooser.APPROVE_OPTION) {
            openedFile = fc.getSelectedFile().getPath();
            txtFile.setText(openedFile);
            log("Opened file: " + openedFile);
            final String fileExtension = Files.getFileExtension(openedFile);
            if ("hdf".equals(fileExtension)) {
                openedFileFormat = FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF4);
                readHdf(openedFile, openedFileFormat);
            } else if ("h5".equals(fileExtension)) {
                openedFileFormat = FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5);
                readHdf(openedFile, openedFileFormat);
            } else {
                log("Chosen file is neither HDF4 nor HDF5 file");
            }
        }
    }

    private void readHdf(String filename, FileFormat fileFormat) {
        FileFormat testFile = HdfFileHelper.openFile(filename, fileFormat);
        TreeNode rootNode = testFile.getRootNode();
        Group root = (Group) ((DefaultMutableTreeNode) rootNode).getUserObject();
        datasetMap.put(HdfParser.DEFAULT_RGB, null);
        findDatasets(root);
        String[] datasets = datasetMap.keySet().toArray(new String[datasetMap.size()]);
        setComboboxModel(cbDatasets, datasets);
        cbDatasets.setSelectedItem(HdfParser.DEFAULT_RGB);
        currentDataset = HdfParser.DEFAULT_RGB;
        currentLatLonRange = currentParser.findLatLonRange(testFile);
        log(currentLatLonRange.toString());
        HdfFileHelper.closeFile(testFile);
    }

    private void findDatasets(Group g) {
        if (g == null)
            return;
        List members = g.getMemberList();
        for (Object member : members) {

            if (member instanceof Dataset) {
                Dataset dataset = (Dataset) member;
                if (currentParser.isDatasetSupported(dataset.getFullName())) {
                    datasetMap.put(dataset.getFullName(), dataset);
                }
            } else if (member instanceof Group) {
                findDatasets((Group) member);
            }
        }
    }

    public void log(String text) {
        taLog.append(text);
        taLog.append("\n");
    }
}
